/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright(c) 2020 - 2023 Allwinner Technology Co.,Ltd. All rights reserved. */
/*
 * Copyright (c) 2007-2018 Allwinnertech Co., Ltd.
 * Author: zhengxiaobin <zhengxiaobin@allwinnertech.com>
 *
 * tvd lowlevel
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include "bsp_tvd.h"

static volatile __tvd_top_dev_t *tvd_top_dev;
static volatile __tvd_dev_t 	 *tvd_device[4];

s32 tvd_top_set_reg_base(unsigned long base)
{
	tvd_top_dev = (__tvd_top_dev_t *)base;
	return 0;
}

s32 tvd_set_reg_base(u32 sel, unsigned long base)
{
	if (sel > 3)
		return -1;

	tvd_device[sel] = (__tvd_dev_t *)base;
	return 0;
}

void tvd_3d_mode(u32 _3d_sel, u32 _3d_en, u32 _3d_addr)
{
	tvd_top_dev->tvd_3d_ctl3.bits.comb_3d_addr0 = _3d_addr;
	tvd_top_dev->tvd_3d_ctl4.bits.comb_3d_addr1 = _3d_addr+0x200000;
	tvd_top_dev->tvd_3d_ctl5.bits.comb_3d_size = 0x200000;
	tvd_top_dev->tvd_3d_ctl1.bits.comb_3d_sel = _3d_sel;
	tvd_top_dev->tvd_3d_ctl1.bits.comb_3d_en = _3d_en;
	tvd_top_dev->tvd_3d_ctl1.bits.tvd_en_3d_dma = _3d_en;
	tvd_device[_3d_sel]->tvd_yc_sep1.bits._3d_comb_filter_dis =
								_3d_en ? 0 : 1;
}

void tvd_enable_chanel(u32 sel, u32 en)
{
	tvd_device[sel]->tvd_en.bits.tvd_en_ch = en;
}

void tvd_input_sel(u32 input)
{
	tvd_top_dev->tvd_top_map.bits.tvin_sel = input;
}

s32 tvd_init(u32 sel, u32 interface)
{
	if (sel == 0 && interface != CVBS_INTERFACE) {
		tvd_adc_config(0, 1);
		tvd_adc_config(1, 1);
		tvd_adc_config(2, 1);
	} else if (interface == CVBS_INTERFACE || sel == 3) {
		tvd_adc_config(sel, 1);
	}

	if (tvd_top_dev->tvd_top_map.bits.tvd_adc_map != 2) {
		tvd_top_dev->tvd_top_map.bits.tvd_adc_map = (interface ? 2 : 1);
		/*
		(sel == 0) ? (interface ? 2 : 1) :
		(sel == 1) ? (interface ? 0 : 1) :
		(sel == 2) ? (interface ? 0 : 1) :
		(interface ? 0 : 1);
		*/
	}
	/*
		if(sel==0)
		{
			tvd_top_dev->tvd_top_map.bits.tvd_adc_map = interface ?
	   2 : 1;
		}
	*/
	tvd_enable_chanel(sel, 1);
	return 0;
}

s32 tvd_deinit(u32 sel, u32 interface)
{
	tvd_enable_chanel(sel, 0);
	if (sel == 0 && interface != CVBS_INTERFACE) {
		tvd_adc_config(0, 0);
		tvd_adc_config(1, 0);
		tvd_adc_config(2, 0);
	} else if (interface == CVBS_INTERFACE || sel == 3) {
		tvd_adc_config(sel, 0);
	}

	return 0;
}

s32 tvd_get_lock(u32 sel)
{
	u32 reg_val;
	u32 lock;
	u32 irq_status;

	tvd_irq_status_get(sel, TVD_IRQ_UNLOCK, &irq_status);
	tvd_irq_status_clear(sel, TVD_IRQ_UNLOCK);
	reg_val = tvd_device[sel]->tvd_status4.dwval;
	if (0xe != (reg_val & 0xf) || (0x1 == irq_status))
		lock = 0;
	else
		lock = 1;
	return lock;
}

void tvd_reset(u32 sel)
{
	volatile u32 delay = 100;
	tvd_device[sel]->tvd_en.bits.tvd_en_ch = 0;
	while (delay--)
		;
	tvd_device[sel]->tvd_en.bits.tvd_en_ch = 1;
}

void tvd_blue_display_mode(u32 sel, u32 mode)
{
	tvd_device[sel]->tvd_mode.bits.blue_display_mode = mode;
}

s32 tvd_get_status(u32 sel, u32 *locked, u32 *system)
{
	if (tvd_device[sel]->tvd_status4.bits.no_signal_detected == 0 &&
	    tvd_device[sel]->tvd_status4.bits.h_locked == 1 &&
	    tvd_device[sel]->tvd_status4.bits.v_locked == 1)
		*locked = 1;
	else
		*locked = 0;

	if (tvd_device[sel]->tvd_status4.bits._625lines_detected == 1)
		*system = 1;
	else
		*system = 0;

	return 0;
}

/* system: 1:pal; 0:NTSC */
s32 tvd_config(u32 sel, u32 interface, u32 system)
{
	tvd_device[sel]->tvd_wb1.bits.wb_en = 0;    /* wb dma disable */
	tvd_device[sel]->tvd_en.bits.tvd_en_ch = 0; /* tvd module disable */

	tvd_device[sel]->tvd_en.bits.en_lock_disable_write_back1only_start_wb_when_locked = 0;
	tvd_device[sel]->tvd_en.bits.en_lock_disable_write_back2when_unlocked = 0;
	tvd_device[sel]->tvd_en.bits.clr_rsmp_fifo = 0;
	tvd_device[sel]->tvd_mode.bits.blue_display_mode = 2; /* auto */
	tvd_device[sel]->tvd_mode.bits.blue_color = 1;	/* black */
	tvd_device[sel]->tvd_clamp_agc1.bits.agc_en = 1;
	tvd_device[sel]->tvd_clamp_agc1.bits.agc_frequence = 1;
	tvd_device[sel]->tvd_clamp_agc1.bits.cagc_en = interface ? 0 : 1;
	tvd_device[sel]->tvd_clamp_agc2.bits.agc_gate_width = 64;
	tvd_device[sel]->tvd_clamp_agc2.bits.agc_backporch_delay = 100;
	tvd_device[sel]->tvd_clamp_agc2.bits.agc_gate_begin = 1666;
	tvd_device[sel]->tvd_hlock1.bits.h_sample_step = 0x20000000;
	tvd_device[sel]->tvd_hlock2.bits.hsync_filter_gate_start_time = 214;
	tvd_device[sel]->tvd_hlock2.bits.hsync_filter_gate_end_time = 78;
	tvd_device[sel]->tvd_hlock3.bits.hsync_rising_detect_window_start_time =
	    45;
	tvd_device[sel]->tvd_hlock3.bits.hsync_rising_detect_window_end_time =
	    80;
	tvd_device[sel]->tvd_hlock3.bits.hsync_tip_detect_window_start_time =
	    233;
	tvd_device[sel]->tvd_hlock3.bits.hsync_tip_detect_window_end_time = 15;
	tvd_device[sel]
	    ->tvd_hlock4.bits
	    .hsync_detect_window_start_time_for_coarse_detection = 0;
	tvd_device[sel]->tvd_hlock4.bits.hsync_detect_window_end_time_for_corase_detect = 128;
	tvd_device[sel]->tvd_hlock4.bits.hsync_rising_time_for_fine_detect = 62;
	tvd_device[sel]->tvd_hlock4.bits.hsync_fine_to_coarse_offset = 62;

	tvd_device[sel]->tvd_mode.bits.ypbpr_mode = interface ? 1 : 0;
	tvd_device[sel]->tvd_mode.bits.svideo_mode = 0;
	tvd_device[sel]->tvd_mode.bits.progressive_mode =
	    (interface == 2) ? 1 : 0;

	tvd_device[sel]->tvd_clamp_agc1.bits.agc_target =  interface ? 0 :
						(system == 0) ? 221 : 220;
	tvd_device[sel]->tvd_clamp_agc1.bits.cagc_target = interface ?
							0 : (system == 0) ?
							160 : 144;
	tvd_device[sel]->tvd_clamp_agc2.bits.black_level_clamp =
					(interface == 0 || system == 0) ? 1 : 0;
	tvd_device[sel]->tvd_hlock2.bits.htol = system;
	tvd_device[sel]->tvd_vlock1.bits.vtol = system;
	tvd_device[sel]->tvd_hlock5.bits.hactive_start = (system ? 137 : 130) -
							(interface ? 20 : 0);
	tvd_device[sel]->tvd_hlock5.bits.hactive_width = 80;
	tvd_device[sel]->tvd_vlock1.bits.vactive_start = system?42:34;
	tvd_device[sel]->tvd_vlock1.bits.vactive_height = system?193:97;
	tvd_device[sel]->tvd_hlock5.bits.backporch_detect_window_start_time = 34;
	tvd_device[sel]->tvd_hlock5.bits.backporch_detect_window_end_time = 66;
	tvd_device[sel]->tvd_vlock2.bits.hsync_dectector_disable_start_line = 112;
	tvd_device[sel]->tvd_vlock2.bits.hsync_detector_disable_end_line = 14;
	tvd_device[sel]->tvd_clock1.bits.color_kill_en = 1;
	tvd_device[sel]->tvd_clock1.bits.color_std = system;
	tvd_device[sel]->tvd_clock1.bits.color_std_ntsc = 0;
	tvd_device[sel]->tvd_clock1.bits.burst_gate_start_time = 50;
	tvd_device[sel]->tvd_clock1.bits.burst_gate_end_time = 70;
	tvd_device[sel]->tvd_clock1.bits.wide_burst_gate = 0;
	tvd_device[sel]->tvd_clock1.bits.chroma_lpf = 1;
	tvd_device[sel]->tvd_clock2.bits.c_sample_step = interface ? 0x0 :
					system ? 0x2a098acb : 0x21f07c1f;
	tvd_device[sel]->tvd_yc_sep1.bits._3d_comb_filter_mode = system ? 4 : 1;
	tvd_device[sel]->tvd_yc_sep1.bits._3d_comb_filter_dis = 1;
	tvd_device[sel]->tvd_yc_sep1.bits._2d_comb_filter_mode = system?6:0;
	tvd_device[sel]->tvd_yc_sep1.bits.secam_notch_wide = 0;
	tvd_device[sel]->tvd_yc_sep1.bits.chroma_bandpass_filter_en = 1;
	tvd_device[sel]->tvd_yc_sep1.bits.pal_chroma_level = 16;
	tvd_device[sel]->tvd_yc_sep1.bits.comb_filter_buffer_clear = 0;
	tvd_device[sel]->tvd_yc_sep1.bits.notch_factor = 0;
	tvd_device[sel]->tvd_yc_sep1.bits._2d_comb_factor = 2;
	tvd_device[sel]->tvd_yc_sep1.bits._3d_comb_factor = system ? 0 : 1;
	tvd_device[sel]->tvd_yc_sep1.bits.chroma_coring_enable = 0;
	tvd_device[sel]->tvd_yc_sep2.bits.horizontal_luma_filter_gain = 2;
	tvd_device[sel]->tvd_yc_sep2.bits.horizontal_chroma_filter_gain = 2;
	tvd_device[sel]->tvd_yc_sep2.bits.luma_vertical_filter_gain = 3;
	tvd_device[sel]->tvd_yc_sep2.bits.chroma_vertical_filter_gain = 3;
	tvd_device[sel]->tvd_yc_sep2.bits.motion_detect_noise_detect_en = 1;
	tvd_device[sel]->tvd_yc_sep2.bits.motion_detect_noise_threshold =
	    system ? 32 : 33;
	tvd_device[sel]->tvd_yc_sep2.bits.noise_detect_en = system ? 0 : 1;
	/* default value:50 */
	tvd_device[sel]->tvd_yc_sep2.bits.noise_threshold = 10;
	tvd_device[sel]->tvd_yc_sep2.bits.luma_noise_factor = 3;
	tvd_device[sel]->tvd_yc_sep2.bits.chroma_noise_factor = 2;
	tvd_device[sel]->tvd_yc_sep2.bits.burst_noise_factor = 0;
	tvd_device[sel]->tvd_yc_sep2.bits.vertical_noise_factor = 0;
	tvd_device[sel]->tvd_enhance1.bits.yc_delay = 0;

	tvd_device[sel]->tvd_enhance1.bits.contrast_gain =
	    tvd_get_contrast(sel);
	tvd_device[sel]->tvd_enhance1.bits.bright_offset = tvd_get_luma(sel);
	if (system == 0) {
		tvd_device[sel]->tvd_enhance1.bits.sharp_en = 1;
		tvd_device[sel]->tvd_enhance1.bits.sharp_coef1 = 5;
		tvd_device[sel]->tvd_enhance1.bits.sharp_coef2 = 2;
		tvd_device[sel]->tvd_enhance3.bits.cbcr_gain_en = 1;
	} else if (system == 1) {
		tvd_device[sel]->tvd_enhance1.bits.sharp_en = 0;
		tvd_device[sel]->tvd_enhance1.bits.sharp_coef1 = 2;
		tvd_device[sel]->tvd_enhance1.bits.sharp_coef2 = 1;
		tvd_device[sel]->tvd_enhance3.bits.cbcr_gain_en = 0;
	}
	tvd_device[sel]->tvd_enhance1.bits.sharp_en = 0;
	tvd_device[sel]->tvd_enhance1.bits.sharp_coef1 = 2;
	tvd_device[sel]->tvd_enhance1.bits.sharp_coef2 = 1;
	tvd_device[sel]->tvd_enhance2.bits.saturation_gain =
	    tvd_get_saturation(sel);
	tvd_device[sel]->tvd_enhance2.bits.chroma_enhance_en = 1;
	tvd_device[sel]->tvd_enhance2.bits.chroma_enhance_strength = 3;
	tvd_device[sel]->tvd_enhance3.bits.cb_gain = 2055;
	tvd_device[sel]->tvd_enhance3.bits.cr_gain = 1457;

	if (interface == 0) {
		tvd_device[sel]->tvd_enhance3.bits.cb_gain = 0x811;
		tvd_device[sel]->tvd_enhance3.bits.cr_gain = 0x5b8;
		tvd_device[sel]->tvd_enhance3.bits.cbcr_gain_en =
		    system ? 0 : 1;
	} else {
		tvd_device[sel]->tvd_enhance3.bits.cb_gain = 1460;
		tvd_device[sel]->tvd_enhance3.bits.cr_gain = 1460;
		tvd_device[sel]->tvd_enhance3.bits.cbcr_gain_en = 1;
	}

	tvd_device[sel]->tvd_debug1.bits.afe_gain_value = 64;
	tvd_device[sel]->tvd_debug1.bits.tvin_lock_debug = 0;
	tvd_device[sel]->tvd_debug1.bits.tvin_lock_high = 0;
	tvd_device[sel]->tvd_debug1.bits.truncation2_reset_gain_enable = 0;
	tvd_device[sel]->tvd_debug1.bits.truncation_reset_gain_enable = 0;
	tvd_device[sel]->tvd_debug1.bits.unlock_reset_gain_enable = 1;
	tvd_device[sel]->tvd_debug1.bits.afe_gain_mode = 0;
	tvd_device[sel]->tvd_debug1.bits.clamp_mode = 0;
	tvd_device[sel]->tvd_debug1.bits.clamp_up_start = 0;
	tvd_device[sel]->tvd_debug1.bits.clamp_dn_start = 0;
	tvd_device[sel]->tvd_debug1.bits.clamp_updn_cycles = 0;
	tvd_device[sel]->tvd_debug2.bits.agc_gate_thresh = 10;
	tvd_device[sel]->tvd_debug2.bits.ccir656_en = 0;
	tvd_device[sel]->tvd_debug2.bits.adc_cbcr_pump_swap = 0;
	tvd_device[sel]->tvd_debug2.bits.adc_updn_swap = 1;
	tvd_device[sel]->tvd_debug2.bits.adc_input_swap = 0;
	tvd_device[sel]->tvd_debug2.bits.hv_dely = 0;
	tvd_device[sel]->tvd_debug2.bits.cv_inv = 0;
	tvd_device[sel]->tvd_debug2.bits.tvd_src = 0;
	tvd_device[sel]->tvd_debug2.bits.adc_wb_mode = 0;
	tvd_device[sel]->tvd_debug2.bits.hue = 0;
	tvd_device[sel]->tvd_debug3.bits.noise_thresh = 50;
	tvd_device[sel]->tvd_debug3.bits.ccir656_cbcr_write_back_sequence = 0;
	tvd_device[sel]->tvd_debug3.bits.cbcr_swap = 0;
	tvd_device[sel]->tvd_debug3.bits.nstd_hysis = 0;
	tvd_device[sel]->tvd_debug4.bits.fixed_burstgate = 1;
	tvd_device[sel]->tvd_debug4.bits.cautopos = 12;
	tvd_device[sel]->tvd_debug4.bits.vnon_std_threshold = 0;
	tvd_device[sel]->tvd_debug4.bits.hnon_std_threshold = 6;
	tvd_device[sel]->tvd_debug4.bits.user_ckill_mode = 0;
	tvd_device[sel]->tvd_debug4.bits.hlock_ckill = 0;
	tvd_device[sel]->tvd_debug4.bits.vbi_ckill = 0;
	tvd_device[sel]->tvd_debug4.bits.chroma_kill = 7;
	tvd_device[sel]->tvd_debug4.bits.agc_gate_vsync_stip = 0;
	tvd_device[sel]->tvd_debug4.bits.agc_gate_vsync_coarse = 1;
	tvd_device[sel]->tvd_debug4.bits.agc_gate_kill_mode = 0;
	tvd_device[sel]->tvd_debug4.bits.agc_peak_en = system ? 1 : 0;
	tvd_device[sel]->tvd_debug4.bits.hstate_unlocked = 1;
	tvd_device[sel]->tvd_debug4.bits.disable_hfine = 0;
	tvd_device[sel]->tvd_debug4.bits.hstate_fixed = 0;
	tvd_device[sel]->tvd_debug4.bits.hlock_vsync_mode = 3;
	tvd_device[sel]->tvd_debug5.bits.vsync_clamp_mode = 0;
	tvd_device[sel]->tvd_debug5.bits.vsync_vbi_lockout_start = 112;
	tvd_device[sel]->tvd_debug5.bits.vsync_vbi_max = 14;
	tvd_device[sel]->tvd_debug5.bits.vlock_wide_range = 0;
	tvd_device[sel]->tvd_debug5.bits.locked_count_clean_max = 4;
	tvd_device[sel]->tvd_debug5.bits.locked_count_noisy_max = 7;
	tvd_device[sel]->tvd_debug5.bits.hstate_max = (system == 0) ? 3 : 5;
	tvd_device[sel]->tvd_debug5.bits.fixed_cstate = 0;
	tvd_device[sel]->tvd_debug5.bits.vodd_delayed = 0;
	tvd_device[sel]->tvd_debug5.bits.veven_delayed = 0;
	tvd_device[sel]->tvd_debug5.bits.field_polarity = 0;
	tvd_device[sel]->tvd_debug6.bits.cstate = 5;
	tvd_device[sel]->tvd_debug6.bits.lose_chromalock_level = 7;
	tvd_device[sel]->tvd_debug6.bits.lose_chromalock_count = 6;
	tvd_device[sel]->tvd_debug6.bits.palsw_level = 2;
	tvd_device[sel]->tvd_debug6.bits.vsync_thresh = 44;
	tvd_device[sel]->tvd_debug6.bits.vsync_cntl = 2;
	tvd_device[sel]->tvd_debug6.bits.vloop_tc = 2;
	tvd_device[sel]->tvd_debug6.bits.field_detect_mode = 2;
	tvd_device[sel]->tvd_debug6.bits.cpump_delay = 185;
	tvd_device[sel]->tvd_debug7.bits.hresampler_2up = 1;
	tvd_device[sel]->tvd_debug7.bits.cpump_adjust_polarity = 0;
	tvd_device[sel]->tvd_debug7.bits.cpump_adjust_delay = 10;
	tvd_device[sel]->tvd_debug7.bits.cpump_adjust = 200;
	tvd_device[sel]->tvd_debug7.bits.cpump_delay_en = 0;
	tvd_device[sel]->tvd_debug7.bits.vf_nstd_en = 1;
	tvd_device[sel]->tvd_debug7.bits.vcr_auto_switch_en = 1;
	tvd_device[sel]->tvd_debug7.bits.mv_hagc = 1;
	tvd_device[sel]->tvd_debug7.bits.dagc_en = 1;
	tvd_device[sel]->tvd_debug7.bits.agc_half_en = 0;
	tvd_device[sel]->tvd_debug7.bits.dc_clamp_mode = 1;
	tvd_device[sel]->tvd_debug7.bits.ldpause_threshold = 0;
	tvd_device[sel]->tvd_debug8.bits.chroma_step_ntsc = 0x21f07c1f;
	tvd_device[sel]->tvd_debug9.bits.chroma_step_paln = 0x21f69446;
	tvd_device[sel]->tvd_debug10.bits.chroma_step_palm = 0x21e6efe3;
	tvd_device[sel]->tvd_debug11.bits.chroma_step_pal = 0x2a098acb;
	tvd_device[sel]->tvd_debug12.bits.y_wb_protect = 0xffffffff;
	tvd_device[sel]->tvd_debug13.bits.uv_wb_protect = 0xffffffff;
	tvd_device[sel]->tvd_debug14.bits.agc_peak_nominal = system ? 12 : 10;
	tvd_device[sel]->tvd_debug14.bits.agc_peak_cntl = 1;
	tvd_device[sel]->tvd_debug14.bits.vsync_agc_lockout_start = 108;
	tvd_device[sel]->tvd_debug14.bits.vsync_agc_max = 16;
	tvd_device[sel]->tvd_debug14.bits.noise_line = 0;
	tvd_device[sel]->tvd_en.bits.tvd_en_ch = 1;
	/* tvd_device[sel]->tvd_debug15.bits.adc_lock_interval_period=0x40; */
	/* tvd_device[sel]->tvd_debug15.bits.adc_lock_interval_low=30; */
	/* tvd_device[sel]->tvd_debug15.bits.adc_lock_mode=1; */

	return 0;
}

s32 tvd_set_wb_width(u32 sel, u32 width)
{
	tvd_device[sel]->tvd_wb2.bits.hactive_num = width;
	return 0;
}

s32 tvd_set_wb_width_jump(u32 sel, u32 width_jump)
{
	tvd_device[sel]->tvd_wb1.bits.hactive_stride = width_jump;
	return 0;
}

s32 tvd_set_wb_height(u32 sel, u32 height)
{
	tvd_device[sel]->tvd_wb2.bits.vactive_num = height;
	return 0;
}

/*
 * @name       tvd_set_wb_addr
 * @brief      set dma y and c(uv) channel addre
 * @param[IN]
 * @param[OUT]
 * @return
 */
s32 tvd_set_wb_addr(u32 sel, u32 addr_y, u32 addr_c)
{
	/* make sure 1 frame change 1 buffer,no need to detect this bit */
	/* while(tvd_device[sel]->tvd_wb1.bits.wb_addr_valid); */
	tvd_device[sel]->tvd_wb3.bits.ch1_y_addr = addr_y;
	tvd_device[sel]->tvd_wb4.bits.ch1_c_addr = addr_c;
	tvd_device[sel]->tvd_wb1.bits.wb_addr_valid = 1;
	return 0;
}

s32 tvd_set_wb_fmt(u32 sel, TVD_FMT_T fmt)
{
	tvd_device[sel]->tvd_wb1.bits.wb_mb_mode =
						(fmt == TVD_MB_YUV420) ? 1 : 0;
	tvd_device[sel]->tvd_wb1.bits.wb_format  =
						(fmt == TVD_PL_YUV422) ? 1 : 0;

	tvd_device[sel]->tvd_wb1.bits.yuv420_fil_en =
	    (fmt == TVD_PL_YUV422) ? 0 : 1;
	return 0;
}

s32 tvd_set_wb_uv_swap(u32 sel, u32 swap)
{
	tvd_device[sel]->tvd_wb1.bits.wb_uv_swap = swap ? 1 : 0;

	return 0;
}

s32 tvd_set_wb_field(u32 sel, u32 is_field_mode, u32 is_field_even)
{
	tvd_device[sel]->tvd_wb1.bits.wb_frame_mode = is_field_mode ? 0 : 1 ;
	tvd_device[sel]->tvd_wb1.bits.hyscale_en =    is_field_mode ? 1 : 0 ;
	return 0;
}

s32 tvd_capture_on(u32 sel)
{
	tvd_device[sel]->tvd_wb1.bits.wb_en = 1;
	return 0;
}

s32 tvd_capture_off(u32 sel)
{
	tvd_device[sel]->tvd_wb1.bits.wb_en = 0;
	return 0;
}

s32 tvd_irq_enable(u32 sel, TVD_IRQ_T irq_id)
{
	tvd_device[sel]->tvd_irq_ctl.dwval |= (1<<irq_id);
	return 0;
}

s32 tvd_irq_disable(u32 sel, TVD_IRQ_T irq_id)
{
	tvd_device[sel]->tvd_irq_ctl.dwval &= ~(1<<irq_id);
	return 0;
}

s32 tvd_irq_status_get(u32 sel, TVD_IRQ_T irq_id, u32 *irq_status)
{
	*irq_status = (tvd_device[sel]->tvd_irq_status.dwval >> irq_id) & 0x1;
	return 0;
}

s32 tvd_irq_status_clear(u32 sel, TVD_IRQ_T irq_id)
{
	tvd_device[sel]->tvd_irq_status.dwval = (1<<irq_id);
	return 0;
}

s32 tvd_dma_irq_status_get(u32 sel, u32 *irq_status)
{
	*irq_status = tvd_device[sel]->tvd_irq_status.dwval;
	return 0;
}

s32 tvd_dma_irq_status_clear_err_flag(u32 sel, u32 irq_status)
{
	tvd_device[sel]->tvd_irq_status.dwval = irq_status;
	return 0;
}

s32 tvd_set_saturation(u32 sel, u32 saturation)
{
	tvd_device[sel]->tvd_enhance2.bits.saturation_gain = saturation;
	return 0;
}

s32 tvd_set_luma(u32 sel, u32 luma)
{
	tvd_device[sel]->tvd_enhance1.bits.bright_offset = luma;
	return 0;
}
s32 tvd_set_contrast(u32 sel, u32 contrast)
{
	tvd_device[sel]->tvd_enhance1.bits.contrast_gain = contrast;
	return 0;
}

u32 tvd_get_saturation(u32 sel)
{
	return tvd_device[sel]->tvd_enhance2.bits.saturation_gain;
}

u32 tvd_get_luma(u32 sel)
{
	return tvd_device[sel]->tvd_enhance1.bits.bright_offset;
}
u32 tvd_get_contrast(u32 sel)
{
	return tvd_device[sel]->tvd_enhance1.bits.contrast_gain;
}

/*
 * @name       tvd_adc_config
 * @brief      config tvd adc and enable it
 * @param[IN]  adc: the index of tvd module
 * @param[IN]  en: 1: enable; 0: disable
 * @return    0 if success
 */
s32 tvd_adc_config(u32 adc, u32 en)
{
	volatile tvd_adc_cfg_reg_t *adc_cfg_p;
	volatile tvd_adc_ctl_reg_t *adc_ctl_p;
	volatile tvd_adc_dig_reg_t *adc_dig_p;

	if (adc > 3 || en > 1)
		return -1;

	adc_cfg_p = (adc == 0) ? &tvd_top_dev->tvd_adc0_cfg :
				(adc == 1) ? &tvd_top_dev->tvd_adc1_cfg :
				(adc == 2) ? &tvd_top_dev->tvd_adc2_cfg :
						&tvd_top_dev->tvd_adc3_cfg;

	adc_ctl_p = (adc == 0) ? &tvd_top_dev->tvd_adc0_ctl :
				(adc == 1) ? &tvd_top_dev->tvd_adc1_ctl :
				(adc == 2) ? &tvd_top_dev->tvd_adc2_ctl :
						&tvd_top_dev->tvd_adc3_ctl;

	adc_dig_p = (adc == 0) ? &tvd_top_dev->tvd_adc0_dig :
				(adc == 1) ? &tvd_top_dev->tvd_adc1_dig :
				(adc == 2) ? &tvd_top_dev->tvd_adc2_dig :
						&tvd_top_dev->tvd_adc3_dig;
	adc_cfg_p->bits.stage1_ibias = 2;
	adc_cfg_p->bits.stage2_ibias = 2;
	adc_cfg_p->bits.stage3_ibias = 2;
	adc_cfg_p->bits.stage4_ibias = 2;
	adc_cfg_p->bits.stage5_ibias = 2;
	adc_cfg_p->bits.stage6_ibias = 2;
	adc_cfg_p->bits.stage7_ibias = 2;
	adc_cfg_p->bits.stage8_ibias = 2;
	adc_cfg_p->bits.clp_step = 7;
	adc_cfg_p->bits.data_dly = 1;
	adc_dig_p->bits.lpf_dig_sel = 0;
	adc_dig_p->bits.lpf_dig_en = en;
	adc_ctl_p->bits.afe_en = en;
	adc_ctl_p->bits.adc_en = en;
	return 0;
}

/*
 * tvd_dbgmode_dump_data
 * Enable tvd debug mode, and dump a specified size section of memory,
 *  and then work back on normal state.
 * @chan_sel     : the index of tvd
 * @mode         : ADC dump mode, 0: dump inmediately,
 *                  1: dump start from the next vsync
 * @dump_dst_addr: the address of the section memory
 * @data_length  : the length of memory,unit half word, must be divisible by 4.
 *
 * Return: 0
 * author : liangyd
 * date : 2016-02-03
 * time:
 * version :
 * history :
 */
u32 tvd_dbgmode_dump_data(u32 chan_sel, u32 mode, uintptr_t dump_dst_addr,
			  u32 data_length)
{
	u32 i = 0;
	/* tvd configuration backup */
	__tvd_dev_t *tvd_dev_bak = kmalloc(sizeof(__tvd_dev_t),
	    GFP_KERNEL | __GFP_ZERO);
	__tvd_top_dev_t *tvd_top_bak = kmalloc(sizeof(__tvd_top_dev_t),
	    GFP_KERNEL | __GFP_ZERO);
	if (!tvd_dev_bak || !tvd_top_bak) {
		pr_warn("%s(), malloc memroy fail\n", __func__);
		kfree(tvd_top_bak);
		kfree(tvd_dev_bak);
		return -1;
	}

	tvd_dev_bak->tvd_debug7.bits.agc_half_en =
	    tvd_device[chan_sel]->tvd_debug7.bits.agc_half_en;
	tvd_dev_bak->tvd_debug4.bits.agc_peak_en =
	    tvd_device[chan_sel]->tvd_debug4.bits.agc_peak_en;
	tvd_dev_bak->tvd_debug7.bits.dagc_en =
	    tvd_device[chan_sel]->tvd_debug7.bits.dagc_en;
	tvd_dev_bak->tvd_clamp_agc1.bits.agc_en =
	    tvd_device[chan_sel]->tvd_clamp_agc1.bits.agc_en;
	tvd_dev_bak->tvd_debug1.bits.afe_gain_value =
	    tvd_device[chan_sel]->tvd_debug1.bits.afe_gain_value;
	tvd_dev_bak->tvd_debug1.bits.afe_gain_mode =
	    tvd_device[chan_sel]->tvd_debug1.bits.afe_gain_mode;
	tvd_dev_bak->tvd_debug1.bits.clamp_mode =
	    tvd_device[chan_sel]->tvd_debug1.bits.clamp_mode;
	tvd_dev_bak->tvd_debug1.bits.tvin_lock_debug =
	    tvd_device[chan_sel]->tvd_debug1.bits.tvin_lock_debug;
	tvd_dev_bak->tvd_debug1.bits.tvin_lock_high =
	    tvd_device[chan_sel]->tvd_debug1.bits.tvin_lock_high;
	tvd_dev_bak->tvd_en.bits.tvd_en_ch =
	    tvd_device[chan_sel]->tvd_en.bits.tvd_en_ch;

	tvd_top_bak->tvd_adc_dump.bits.adc_test_mode =
	    tvd_top_dev->tvd_adc_dump.bits.adc_test_mode;
	tvd_top_bak->tvd_3d_ctl1.bits.tvd_en_3d_dma =
	    tvd_top_dev->tvd_3d_ctl1.bits.tvd_en_3d_dma;
	tvd_top_bak->tvd_3d_ctl1.bits.comb_3d_en =
	    tvd_top_dev->tvd_3d_ctl1.bits.comb_3d_en;
	tvd_top_bak->tvd_3d_ctl4.bits.comb_3d_addr1 =
	    tvd_top_dev->tvd_3d_ctl4.bits.comb_3d_addr1;
	tvd_top_bak->tvd_3d_ctl5.bits.comb_3d_size =
	    tvd_top_dev->tvd_3d_ctl5.bits.comb_3d_size;
	tvd_top_bak->tvd_3d_ctl2.bits.dram_trig =
	    tvd_top_dev->tvd_3d_ctl2.bits.dram_trig;
	tvd_top_bak->tvd_3d_ctl1.bits.comb_3d_sel =
	    tvd_top_dev->tvd_3d_ctl1.bits.comb_3d_sel;
	tvd_top_bak->tvd_3d_ctl1.bits.comb_3d_sel =
	    tvd_device[chan_sel]->tvd_en.bits.tvd_en_ch;
	tvd_top_bak->tvd_adc_dump.bits.adc_dump_mode =
	    tvd_top_dev->tvd_adc_dump.bits.adc_dump_mode;
	tvd_top_bak->tvd_adc_dump.bits.adc_wb_length =
	    tvd_top_dev->tvd_adc_dump.bits.adc_wb_length;

	/* tvd device agc configuration */
	tvd_device[chan_sel]->tvd_debug7.bits.agc_half_en = 0;
	tvd_device[chan_sel]->tvd_debug4.bits.agc_peak_en = 0;
	tvd_device[chan_sel]->tvd_debug7.bits.dagc_en = 0;
	tvd_device[chan_sel]->tvd_clamp_agc1.bits.agc_en = 0;
	tvd_device[chan_sel]->tvd_debug1.bits.afe_gain_value = 64;
	tvd_device[chan_sel]->tvd_debug1.bits.afe_gain_mode = 1;

	/* tvd clamp mode configuration */
	tvd_device[chan_sel]->tvd_debug1.bits.clamp_mode = 1;

	/* tvd lock configuration */
	tvd_device[chan_sel]->tvd_debug1.bits.tvin_lock_debug = 1;
	tvd_device[chan_sel]->tvd_debug1.bits.tvin_lock_high = 0;

	/* delay for lock finish */
	msleep(50);

	while (tvd_top_dev->tvd_adc_dump.bits.adc_wb_start)
		;

	tvd_top_dev->tvd_adc_dump.bits.adc_test_mode = 1;

	tvd_top_dev->tvd_3d_ctl1.bits.tvd_en_3d_dma = 0;
	tvd_top_dev->tvd_3d_ctl1.bits.comb_3d_en = 0;

	tvd_top_dev->tvd_3d_ctl4.bits.comb_3d_addr1 = dump_dst_addr;
	tvd_top_dev->tvd_3d_ctl5.bits.comb_3d_size = 0x800000 * 2;
	tvd_top_dev->tvd_3d_ctl2.bits.dram_trig = 0x1000;
	tvd_top_dev->tvd_3d_ctl1.bits.comb_3d_sel = chan_sel;

	tvd_top_dev->tvd_3d_ctl1.bits.tvd_en_3d_dma = 1;
	tvd_top_dev->tvd_3d_ctl1.bits.comb_3d_en = 1;

	tvd_device[chan_sel]->tvd_en.bits.tvd_en_ch = 1;

	tvd_top_dev->tvd_adc_dump.bits.adc_dump_mode = mode;
	tvd_top_dev->tvd_adc_dump.bits.adc_wb_length = data_length & (~0xff);
	tvd_top_dev->tvd_adc_dump.bits.adc_wb_buffer_reset = 1;
	/* delay for buffer reset */
	for (i = 0; i < 20; i++)
		;
	tvd_top_dev->tvd_adc_dump.bits.adc_wb_buffer_reset = 0;

	tvd_top_dev->tvd_adc_dump.bits.adc_wb_start = 1;
	/* wait for dump finish */
	while (tvd_top_dev->tvd_adc_dump.bits.adc_wb_start)
		;

	/* reset tvd configuration */
	tvd_device[chan_sel]->tvd_debug7.bits.agc_half_en =
	    tvd_dev_bak->tvd_debug7.bits.agc_half_en;
	tvd_device[chan_sel]->tvd_debug4.bits.agc_peak_en =
	    tvd_dev_bak->tvd_debug4.bits.agc_peak_en;
	tvd_device[chan_sel]->tvd_debug7.bits.dagc_en =
	    tvd_dev_bak->tvd_debug7.bits.dagc_en;
	tvd_device[chan_sel]->tvd_clamp_agc1.bits.agc_en =
	    tvd_dev_bak->tvd_clamp_agc1.bits.agc_en;
	tvd_device[chan_sel]->tvd_debug1.bits.afe_gain_value =
	    tvd_dev_bak->tvd_debug1.bits.afe_gain_value;
	tvd_device[chan_sel]->tvd_debug1.bits.afe_gain_mode =
	    tvd_dev_bak->tvd_debug1.bits.afe_gain_mode;
	tvd_device[chan_sel]->tvd_debug1.bits.clamp_mode =
	    tvd_dev_bak->tvd_debug1.bits.clamp_mode;
	tvd_device[chan_sel]->tvd_debug1.bits.tvin_lock_debug =
	    tvd_dev_bak->tvd_debug1.bits.tvin_lock_debug;
	tvd_device[chan_sel]->tvd_debug1.bits.tvin_lock_high =
	    tvd_dev_bak->tvd_debug1.bits.tvin_lock_high;
	tvd_device[chan_sel]->tvd_en.bits.tvd_en_ch =
	    tvd_dev_bak->tvd_en.bits.tvd_en_ch;

	tvd_top_dev->tvd_adc_dump.bits.adc_test_mode =
	    tvd_top_bak->tvd_adc_dump.bits.adc_test_mode;
	tvd_top_dev->tvd_3d_ctl4.bits.comb_3d_addr1 =
	    tvd_top_bak->tvd_3d_ctl4.bits.comb_3d_addr1;
	tvd_top_dev->tvd_3d_ctl5.bits.comb_3d_size =
	    tvd_top_bak->tvd_3d_ctl5.bits.comb_3d_size;
	tvd_top_dev->tvd_3d_ctl2.bits.dram_trig =
	    tvd_top_bak->tvd_3d_ctl2.bits.dram_trig;
	tvd_top_dev->tvd_3d_ctl1.bits.comb_3d_sel =
	    tvd_top_bak->tvd_3d_ctl1.bits.comb_3d_sel;
	tvd_top_dev->tvd_3d_ctl1.bits.comb_3d_sel =
	    tvd_dev_bak->tvd_en.bits.tvd_en_ch;
	tvd_top_dev->tvd_adc_dump.bits.adc_dump_mode =
	    tvd_top_bak->tvd_adc_dump.bits.adc_dump_mode;
	tvd_top_dev->tvd_adc_dump.bits.adc_wb_length =
	    tvd_top_bak->tvd_adc_dump.bits.adc_wb_length;
	tvd_top_dev->tvd_3d_ctl1.bits.tvd_en_3d_dma =
	    tvd_top_bak->tvd_3d_ctl1.bits.tvd_en_3d_dma;
	tvd_top_dev->tvd_3d_ctl1.bits.comb_3d_en =
	    tvd_top_bak->tvd_3d_ctl1.bits.comb_3d_en;

	kfree(tvd_top_bak);
	kfree(tvd_dev_bak);

	return 0;
}

void tvd_agc_auto_config(u32 sel)
{
	tvd_device[sel]->tvd_debug7.bits.agc_half_en = 1;
	tvd_device[sel]->tvd_debug4.bits.agc_peak_en = 1;
	tvd_device[sel]->tvd_debug7.bits.dagc_en = 1;
	tvd_device[sel]->tvd_clamp_agc1.bits.agc_en = 1;
	tvd_device[sel]->tvd_debug1.bits.afe_gain_mode = 0;
}

void tvd_agc_manual_config(u32 sel, u32 agc_manual_val)
{
	tvd_device[sel]->tvd_debug7.bits.agc_half_en = 0;
	tvd_device[sel]->tvd_debug4.bits.agc_peak_en = 0;
	tvd_device[sel]->tvd_debug7.bits.dagc_en = 0;
	tvd_device[sel]->tvd_clamp_agc1.bits.agc_en = 0;
	tvd_device[sel]->tvd_debug1.bits.afe_gain_mode = 1;
	tvd_device[sel]->tvd_debug1.bits.afe_gain_value = agc_manual_val;
}

void tvd_cagc_config(u32 sel, u32 enable)
{
	tvd_device[sel]->tvd_clamp_agc1.bits.cagc_en = enable;
}
